iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0

elf loader
今天延續昨天的work,我們要來將elf loader的內容補完,
根據昨天的內容,我們要有能力去判斷我們需要的是elf file內的哪一個content,幸好只要引用"elf.h"就可以幫助我們簡單的得到elf header的格式及內容,昨天我們透過e_entry得到了entrypoint的位置。

在今天,我們要將elf檔案內所要被處理器所執行的內容讀取出來,放到記憶體中。我們會去抓elf檔案內的program header (phdr),phdr內包含了segment的資訊,我們可以透過elf header的e_phum得到對應的section數量,而phdr的格式如下 (參照 linux manual page elf)

  typedef struct {
               uint32_t   p_type;
               uint32_t   p_flags;
               Elf64_Off  p_offset;
               Elf64_Addr p_vaddr;
               Elf64_Addr p_paddr;
               uint64_t   p_filesz;
               uint64_t   p_memsz;
               uint64_t   p_align;
           } Elf64_Phdr;

其中各欄位的內容大致如下。

属性 描述
p_type 區段的類型,代表如何解釋該段,有下列數種。
PT_NULL 未使用,該類型允許了有未被使用的區段
PT_LOAD 可加載段,同時也是處理器主要需要的區段
PT_DYNAMIC 動態連結訊息,供連結器使用(Linker)
PT_INTERP 紀錄 program interpreter 路徑與檔名。
PT_NOTE 紀錄 Note的位置,不重要。
PT_SHLIB 保留但沒有語意 (reserved but has unspecified semantics)。
PT_PHDR PHDR本身也需要一個位置放 。
PT_LOPROC, PT_HIPROC 範圍內的值用來處理特定的處理器語意,。
PT_GNU_STACK Linux使用的GNU擴充,用來控制stack的狀態。
p_offset 區段的位置的偏移量 (與文件開頭的距離)。
p_vaddr 區段在虛擬記憶體的位置。
p_paddr 區段在實體記憶體的位置的。
p_filesz file所佔的大小。
p_memsz 所需要的memory大小。
p_flags 該區段的權限相關flag:
PF_X 可執行。
PF_W 可寫。
PF_R 可讀。
p_align 該區段需要對齊的值。

實作

我們只要讀取e_phoffset,知道phdr的位置,再根據每一個section的offset就可以得出我們所需要的區段了,不過在實作我們一樣先加一個測試,測試內容為昨天所讀到的entry point位置(0x10116)對應的內容。

那我們要怎麼知道答案應該是多少呢,我們可以透過objdump得到elf的內容,這裡可以看到0x10116的值是0x00004197。

riscv64-unknown-elf-objdump -d HelloWorld.elf  > HelloWorld.objdump

https://ithelp.ithome.com.tw/upload/images/20230928/20162436eJiR7pB8Ht.png

因此我們撰寫的測試內容如下

unsigned get_mem_w(unsigned long long  int addr)
{
    return *(uint32_t*)(ALISS::memory + addr);
}

TEST(MyTestSuite, ELFLoaderTest_FirstInst) {
    const char* test = "test.elf";
    ALISS::loadElf(test);
    EXPECT_EQ(get_mem_w(0x10016), 0x4197);
}

接下來開始實作啦,實作的程式碼如下

for (int i = 0; i < elfHeader.e_phnum; i++) {
        file.seekg(elfHeader.e_phoff + i * sizeof(Elf64_Phdr));

        Elf64_Phdr programHeader;
        file.read(reinterpret_cast<char*>(&programHeader), sizeof(Elf64_Phdr));

        // Check if this is a loadable segment
        if (programHeader.p_type == PT_LOAD) {
            // Save flags, address, and size
            Elf64_Word flags = programHeader.p_flags;
            Elf64_Addr addr = programHeader.p_vaddr;
            Elf64_Xword size = programHeader.p_memsz;

            // Seek to the segment's file offset
            file.seekg(programHeader.p_offset);

            // Read the segment data to memory
            file.read(reinterpret_cast<char*>(ALISS::memory + addr), size);
        }
    }

如同前面所說的,讀取e_phnum知道區段數量,跳到對應的位置,如果該段的type是PT_LOAD的話則讀到memory內。

新增後測試通過,我們已經實現了一個簡易版的ELF Loader啦~~(灑花)

https://ithelp.ithome.com.tw/upload/images/20230928/201624364Anbq7P8Vv.png


碎碎念 : 明天終於要開始實作指令了,看能不能趁連假多偷一點進度回來。


上一篇
Day 12 - ELF Loader (1 / 2)
下一篇
Day 14 - CPU Pipeline
系列文
從零開始的RISC-V ISA Simulator (Another Little RISC-V ISA Simulator)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言